{% set x = c.ctx %}

A habit is an activity with its log button switched on. Add one on the Habits page (name + rhythm), or tick one-tap button on any activity above; set its time from to habit if minutes matter. Everything below is about letting other apps fill it in.

Calendar feeds

Practice apps, habit trackers and time trackers can publish what they record as a calendar (look for subscribe, publish or export to calendar — a webcal:// link). Paste that link here, say which habit it feeds and which entry titles count, and Latch reads it every {{ x.interval }} minutes. Each entry becomes one log, stamped when it ended, carrying its length. Re-reading is safe: an entry is written once, and one that later moves or grows has its old row struck through and a new one written. Recurring entries are skipped — a schedule is a plan, not a record. Anything older than two days when it arrives counts for streaks and totals but earns no XP.

{% if x.feeds %}
{% for f in x.feeds %}
{% endfor %}
habitlabel · urltitle containslast fetch

{%- if f.last_fetch_at %}{{ f.last_fetch_at|rel(now) }} {%- if f.last_status == 'ok' %} · ok {{ f.last_seen }} seen · {{ f.last_matched }} matched · {{ f.last_new }} new {%- else %} · error {{ f.last_error }}{% endif %} {%- else %}never{% endif -%}
{% endif %} {% if x.habits %}

Several habits can read the same calendar — one feed row per habit, each with its own title contains. The calendar is fetched once per pass however many rows share it.

Import a calendar file

No network at all: export from the other app, upload it here. The same file twice imports nothing twice.

{% else %}

Add a habit first — a feed needs something to log into.

{% endif %} {% if x.egress %}

🌐 What this module reaches. {{ x.egress }} {% if hosted %}
On a hosted instance a feed URL must be on the public internet — private and local addresses are refused.{% endif %}

{% endif %}

📅 Standing days bike on Mon, Wed, Fri — your Handler issues the order

On each day you tick, an order for that habit lands in Training, due at midnight. Logging the habit completes it — a ride logged before the order appeared still counts. Let it run out while you log a different habit and that is 🌀 Zoomies — see Mischief.

{% if x.habits %}
{% for d in ['mon','tue','wed','thu','fri','sat','sun'] %}{% endfor %} {% for a in x.habits %}{% set on = (a.order_days or '').split(',') %} {% for d in ['mon','tue','wed','thu','fri','sat','sun'] %}{% endfor %} {% endfor %}
{{ d|title }}min
{{ a.icon }} {{ a.title }}
{% else %}

Add a habit first.

{% endif %}